package com.yanyu.space.sys.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yanyu.space.common.auth.support.AuthUtil;
import com.yanyu.space.common.auth.bean.enums.AuthType;
import com.yanyu.space.common.core.bean.auth.SpaceUser;
import com.yanyu.space.common.core.bean.db.PageEntity;
import com.yanyu.space.common.core.bean.web.R;
import com.yanyu.space.common.core.bean.web.RCode;
import com.yanyu.space.common.core.exception.DataNotFoundException;
import com.yanyu.space.common.core.exception.ServiceException;
import com.yanyu.space.common.core.util.FileUtil;
import com.yanyu.space.common.db.pagination.PageUtil;
import com.yanyu.space.file.bean.bo.FileDirectSearchBo;
import com.yanyu.space.file.bean.bo.FileInfoBo;
import com.yanyu.space.file.feign.IFileClient;
import com.yanyu.space.sys.bean.enums.EnabledEnum;
import com.yanyu.space.sys.bean.enums.SysRCode;
import com.yanyu.space.sys.bean.po.*;
import com.yanyu.space.sys.bean.vo.user.*;
import com.yanyu.space.sys.dao.*;
import com.yanyu.space.sys.service.IDeptService;
import com.yanyu.space.sys.service.IUserService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 系统用户 业务实现类
 *
 * @author yanyu
 */
@Service
@Slf4j
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    private RoleMapper roleMapper;
    private PostMapper postMapper;
    private RoleUserMapper roleUserMapper;
    private UserPostMapper userPostMapper;
    private IDeptService deptService;
    private IFileClient fileClient;

    @Override
    public PageEntity<UserInfoVo> listPage(UserSearchVo vo) {
        // 查询部门及其子部门
        List<Dept> depts = deptService.listChildren(vo.getDeptId());
        List<Long> deptIds = depts.parallelStream().map(Dept::getId).collect(Collectors.toList());
        IPage<User> page = baseMapper.listPage(PageUtil.getParam(), vo, deptIds);
        return PageUtil.getResult(page, UserInfoVo.class);
    }

    @Override
    public UserInfoVo getById(Long userId) {
        User user = baseMapper.selectById(userId);
        UserInfoVo vo = BeanUtil.copyProperties(user, UserInfoVo.class);
        if (vo != null) {
            if (user.getDeptId() != null) {
                Dept dept = deptService.getById(user.getDeptId());
                if (dept != null) {
                    vo.setDeptName(dept.getDeptName());
                }
            }
            List<Role> roles = roleMapper.selectList(Wrappers.<Role>lambdaQuery().orderByAsc(Role::getUpdateTime));
            vo.setRoles(roles);
            List<Post> posts = postMapper.selectList(Wrappers.<Post>lambdaQuery().orderByAsc(Post::getSort));
            vo.setPosts(posts);
            if (userId != null) {
                List<RoleUser> roleUsers = roleUserMapper.selectList(Wrappers.<RoleUser>lambdaQuery().eq(RoleUser::getUserId, userId));
                List<UserPost> userPosts = userPostMapper.selectList(Wrappers.<UserPost>lambdaQuery().eq(UserPost::getUserId, userId));
                List<Long> roleIds = roleUsers.parallelStream().map(RoleUser::getRoleId).collect(Collectors.toList());
                List<Long> postIds = userPosts.parallelStream().map(UserPost::getPostId).collect(Collectors.toList());
                vo.setRoleIds(roleIds);
                vo.setPostIds(postIds);
            }
        }
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(UserAddVo vo) {
        // 校验用户名是否存在
        checkUserName(null, vo.getUserName());

        // 新增用户
        User user = BeanUtil.copyProperties(vo, User.class);
        user.setPasswordValid(LocalDateTime.now().minusYears(2));
        baseMapper.insert(user);

        // 新增角色关联关系
        if (CollectionUtils.isNotEmpty(vo.getRoleIds())) {
            for (Long roleId : vo.getRoleIds()) {
                RoleUser roleUser = new RoleUser();
                roleUser.setUserId(user.getId());
                roleUser.setRoleId(roleId);
                roleUserMapper.insert(roleUser);
            }
        }

        // 新增岗位关系
        if (CollectionUtils.isNotEmpty(vo.getPostIds())) {
            for (Long postId : vo.getPostIds()) {
                UserPost userPost = new UserPost();
                userPost.setPostId(postId);
                userPost.setUserId(user.getId());
                userPostMapper.insert(userPost);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateById(UserUpdateVo vo) {
        // 校验用户名是否存在
        checkUserName(vo.getId(), vo.getUserName());

        // 新增用户
        User user = BeanUtil.copyProperties(vo, User.class);
        user.setPasswordValid(LocalDateTime.now().minusYears(2));
        baseMapper.update(user, Wrappers.<User>lambdaUpdate().set(User::getDeptId, user.getDeptId()).eq(User::getId, user.getId()));

        // 新增角色关联关系
        roleUserMapper.delete(Wrappers.<RoleUser>lambdaQuery().eq(RoleUser::getUserId, vo.getId()));
        if (CollectionUtils.isNotEmpty(vo.getRoleIds())) {
            for (Long roleId : vo.getRoleIds()) {
                RoleUser roleUser = new RoleUser();
                roleUser.setUserId(user.getId());
                roleUser.setRoleId(roleId);
                roleUserMapper.insert(roleUser);
            }
        }

        // 新增岗位关系
        userPostMapper.delete(Wrappers.<UserPost>lambdaQuery().eq(UserPost::getUserId, vo.getId()));
        if (CollectionUtils.isNotEmpty(vo.getPostIds())) {
            for (Long postId : vo.getPostIds()) {
                UserPost userPost = new UserPost();
                userPost.setPostId(postId);
                userPost.setUserId(user.getId());
                userPostMapper.insert(userPost);
            }
        }

        // 更新session
        updateSession(user.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(UserUpdateVo vo) {
        User user = new User();
        user.setId(vo.getId());
        user.setEnabled(vo.getEnabled());
        baseMapper.updateById(user);
        if(EnabledEnum.NO.match(user.getEnabled())) {
            AuthUtil.kickOut(vo.getId(), AuthType.ADMIN_TYPE);
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resetPwd(UserResetPwdVo vo) {
        User user = new User();
        user.setId(vo.getUseId());
        user.setPassword(vo.getPassword());
        baseMapper.updateById(user);
        AuthUtil.kickOut(vo.getUseId(), AuthType.ADMIN_TYPE);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(List<Long> ids) {
        for (Long id : ids) {
            User user = baseMapper.selectById(id);
            if (user.getSysFlag().equals(1)) {
                throw new ServiceException(SysRCode.USER_ADMIN, user.getUserName());
            }
            baseMapper.deleteById(id);
            roleUserMapper.delete(Wrappers.<RoleUser>lambdaQuery().eq(RoleUser::getUserId, id));
            userPostMapper.delete(Wrappers.<UserPost>lambdaQuery().eq(UserPost::getUserId, id));
            AuthUtil.kickOut(id, AuthType.ADMIN_TYPE);
        }
    }

    @Override
    public PageEntity<User> listAllocated(UserAllocatedRoleVo vo) {
        IPage<User> page = baseMapper.listAllocated(PageUtil.getParam(), vo);
        return PageUtil.getResult(page);
    }

    @Override
    public PageEntity<User> listUnallocated(UserUnallocatedRoleVo vo) {
        IPage<User> page = baseMapper.listUnallocated(PageUtil.getParam(), vo);
        return PageUtil.getResult(page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAllocatedRole(Long userId, List<Long> roleIds) {
        User user = getById(userId);
        if (user == null) {
            throw new DataNotFoundException();
        }
        if (user.getSysFlag().equals(1)) {
            throw new ServiceException(SysRCode.USER_ADMIN, user.getUserName());
        }

        roleUserMapper.delete(Wrappers.<RoleUser>lambdaQuery().eq(RoleUser::getUserId, userId));
        for (Long roleId : roleIds) {
            RoleUser roleUser = new RoleUser();
            roleUser.setUserId(userId);
            roleUser.setRoleId(roleId);
            roleUserMapper.insert(roleUser);
        }
    }

    @SneakyThrows
    @Override
    public void avatar(MultipartFile file) {
        Long loginId = StpUtil.getLoginIdAsLong();
        MultipartFile multipartFile = FileUtil.byteToMultipartFile(file.getBytes(), String.valueOf(loginId), file.getContentType());
        FileDirectSearchBo bo = new FileDirectSearchBo();
        bo.setFiles(new MultipartFile[]{multipartFile});
        R<List<FileInfoBo>> result = fileClient.directUpload(bo);
        log.info("调用file服务上传头像响应结果 -> {}", JSONObject.toJSONString(result));
        if (result.getCode() != RCode.SUCCESS.getCode() || CollectionUtils.isEmpty(result.getBean())) {
            throw new ServiceException(SysRCode.UPDATE_AVATAR);
        }
        FileInfoBo fileInfoBo = result.getBean().get(0);
        User user = new User();
        user.setId(loginId);
        user.setAvatar(StringUtils.join("http://minio:9000/", fileInfoBo.getFileUrl()));
        baseMapper.updateById(user);
        updateSession(loginId);
    }

    /**
     * 用户信息是否重复
     *
     * @param userId
     * @param userName
     */
    private void checkUserName(Long userId, String userName) {
        long userNameCnt =
                baseMapper.selectCount(Wrappers.<User>lambdaQuery().ne(userId != null, User::getId, userId).eq(User::getUserName, userName));
        if (userNameCnt > 0) {
            throw new ServiceException(SysRCode.USER_CODE_EXIST);
        }
    }

    private void updateSession(Long userId) {
        User user = baseMapper.selectById(userId);
        SpaceUser spaceUser = SpaceUser.builder().id(user.getId())
                .userName(user.getUserName()).deptId(user.getDeptId())
                .sysFlag(user.getSysFlag()).avatar(user.getAvatar()).build();
        AuthUtil.resetSession(spaceUser, AuthType.ADMIN_TYPE);
    }
}
